﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ImageCompressor
{
    /// <summary>
    /// 后台线程运行状态
    /// </summary>
    enum RunningState
    {
        NoFile,
        Ready,
        Running,
        WaitStop,
        AllThreadStoped,
        Finished,
    }

    /// <summary>
    /// 管理读取IMG文件，并且负责Invoke压缩多线程
    /// </summary>
    class FilesManager
    {
        
        public bool IncludeSubFolders { get; set; } = true;
        

        ImageCompressorForm owner { get; set; }



        public FilesManager(ImageCompressorForm owner)
        {
            this.owner = owner;
        }

        #region Files

        /// <summary>
        /// 储存读取的所有照片的Size
        /// </summary>
        public long PreAllImageSizeinKB { get; private set; }


        /// <summary>
        /// 所有的照片的路径（打开路径和储存路径）
        /// </summary>
        List<ImgPaths> ImgPathList { get; set; } = new List<ImgPaths>();
        /// <summary>
        /// 总共有多少张照片
        /// </summary>
        public int FilesCount => ImgPathList.Count;
        
        /// <summary>
        /// 开始读取照片
        /// 从输入的文件夹里，提取出所有的照片路径
        /// </summary>
        /// <param name="folders"></param>
        public void ReadAllFilePaths(IEnumerable<string> folders)
        {
            ImgPathList.Clear();
            processList = null;
            PreAllImageSizeinKB = 0;
            foreach (string folderPath in folders)
            {
                string[] files;
                if (IncludeSubFolders)
                    files = Directory.GetFiles(folderPath, "*.*", SearchOption.AllDirectories).Where(x => IsImg(Path.GetExtension(x))).ToArray();
                else
                    files = Directory.GetFiles(folderPath, "*.*", SearchOption.TopDirectoryOnly).Where(x => IsImg(Path.GetExtension(x))).ToArray();
                foreach (string file in files) { PreAllImageSizeinKB += new FileInfo(file).Length / 1024; }
                DirectoryInfo info = new DirectoryInfo(folderPath);
                string newFolder = Path.Combine(info.Parent.FullName, info.Name + Localize.Get("_Compressed"));
                ImgPathList.AddRange(files.Select(x => new ImgPaths(x, x.Replace(folderPath, newFolder))));

            }
            progress = new CountProgress(ImgPathList.Count, owner);
        }


        

        static long skipFileSize = 900 * 1024;
        bool IsImg(string extension)
        {
            switch (extension.ToLower())
            {
                case ".jpg":
                case ".png":
                case ".jpeg":
                case ".bmp":
                    return true;
            }
            return false;
        }
        /// <summary>
        /// 设定跳过的文件大小
        /// </summary>
        /// <param name="textInKB"></param>
        public void SetSkipFileSizeInKB(string textInKB)
        {
            if (int.TryParse(textInKB, out int result))
                skipFileSize = result * 1024;
        }

        public int CurrentSkipFileSizeInKB => (int)(skipFileSize / 1024);

        /// <summary>
        /// 判断是否需要跳过该文件
        /// </summary>
        /// <param name="fileInfo"></param>
        /// <returns></returns>
        public static bool IsSkip(FileInfo fileInfo)
        {
            return fileInfo == null || fileInfo.Length < skipFileSize;
        }
        /// <summary>
        /// 格式化显示大小
        /// </summary>
        /// <param name="sizeInKB"></param>
        /// <returns></returns>
        public string GetFileSizeString(long sizeInKB)
        {
            if (sizeInKB > 1048576)
                return string.Format("{0:f2}GB", sizeInKB / 1048576.0);
            else if (sizeInKB > 1024)
                return string.Format("{0:f2}MB", sizeInKB / 1024.0);
            else return string.Format("{0:f2}KB", sizeInKB);
        }


        #endregion

        #region Preview

        /// <summary>
        /// 用于储存临时保存的预览压缩图片
        /// </summary>
        string TempImgPath = Path.Combine(Path.GetTempPath(), "temp.jpg");

        /// <summary>
        /// 返回当前所有路径的随机序号，如果没有路径，return false
        /// </summary>
        /// <param name="seed"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public bool GetRandomIndex(int seed, out int index)
        {
            if (FilesCount > 0)
            {
                Random random = new Random(seed);
                index = random.Next(0, FilesCount);
                return true;
            }

            index = -1;
            return false;
        }

        public Image GetImageAtIndex(int index, out string fileSize)
        {
            FileInfo rawFile = ImgPathList[index].ReadFileInfo;
            if (rawFile.Exists)
            {
                using (FileStream stream = rawFile.OpenRead())
                {
                    Image raw = Image.FromStream(stream);

                    fileSize = GetFileSizeString(stream.Length / 1024);
                    stream.Close();
                    return raw;
                }

            }
            else
            {
                fileSize = Localize.Get("Missing");
                return null;
            }
        }

        /// <summary>
        /// 将列表中Index序号的图像进行压缩
        /// 储存到临时文件里去
        /// 然后读取该临时图片作为输出
        /// 失败返回null
        /// skip时返回null,fileSize = null;
        /// </summary>
        /// <param name="index"></param>
        /// <param name="fileSize"></param>
        /// <param name="skip"></param>
        /// <returns></returns>
        public Image GetCompressedImgAtIndex(int index, out string fileSize, out bool skip, out string errorMessage)
        {

            fileSize = null;
            FileInfo rawFile = ImgPathList[index].ReadFileInfo;
            skip = IsSkip(rawFile);
            if (skip)
            {

                errorMessage = null;
                return null;
            }
            JPGCompressor compressor = new JPGCompressor(this.CompressLevel, this.MaxWidth, this.MaxHeight);

            if (!compressor.ReadFileToImage(rawFile, out Bitmap image, out _, out errorMessage))
            {
                return null;
            }
            Bitmap bitmap = compressor.CompressImgToBitmap(image);
            //image.Dispose();
            if (compressor.SaveCompressBitmap(bitmap, new FileInfo(TempImgPath), out long saveSizeInKB, out errorMessage))
            {
                fileSize = GetFileSizeString(saveSizeInKB);
            }
            else fileSize = Localize.Get("Estimate Fail");
            return bitmap;
            
        }

        #endregion

        #region MultiThread
        /// <summary>
        /// 储存照片时是否保留源文件
        /// </summary>
        public bool KeepOriginal { get; set; } = true;
        int compressLevel = 8;
        /// <summary>
        /// 当前的压缩级数
        /// </summary>
        public int CompressLevel
        {
            get
            {
                return compressLevel;
            }
            set
            {
                compressLevel = value;
                MaxWidth = JPGCompressor.CompressionLevelToMaxWidth(compressLevel);
                MaxHeight = JPGCompressor.CompressionLevelToMaxHeight(compressLevel);
            }
        }
        /// <summary>
        /// 当前的最大宽度
        /// </summary>
        public int MaxWidth { get; set; }
        /// <summary>
        /// 当前的最大高度
        /// </summary>
        public int MaxHeight { get; set; }


        /// <summary>
        /// 向Winform标记运行状态
        /// </summary>
        public RunningState State
        {
            get
            {
                if(FilesCount == 0)
                {
                    return RunningState.NoFile;
                }
                else if (processList == null)
                {
                    return RunningState.Ready;
                }
                else if (progress.Finished) return RunningState.Finished;
                else
                {
                    foreach (ImgsProcess process in processList)
                    {
                        if (!process.Stoped)
                        {
                            if(process.ToStop)return RunningState.WaitStop;
                            return RunningState.Running;
                        }
                    }
                    return RunningState.AllThreadStoped;
                }
            }
        }

        /// <summary>
        /// 用来储存多线程运行过程数据和结果
        /// </summary>
        internal CountProgress progress;

        /// <summary>
        /// 所有线程运行数据
        /// </summary>
        ImgsProcess[] processList;

        /// <summary>
        /// 多线程常量，增加貌似并没有加速很多，猜测卡在JPG的IO读取上。
        /// </summary>
        public const int ThreadCount = 16;

        /// <summary>
        /// 正式压缩所有图片
        /// 返回线程数
        /// </summary>
        public void RunAllCompress()
        {
            //运行后不能修改KeepOriginal,CompressLevel
            foreach (ImgPaths path in ImgPathList) path.KeepOriginal = KeepOriginal;
            


            //将所有的图片Path，平均分配到【线程数】个ProcessImgs里

            if (State == RunningState.Ready)
            {
                progress.stopwatch.Restart();
                processList = DivideIntoMultiProcess();
            }
            else progress.stopwatch.Start();
            //另一种情况是Stoped，就继续上次计算

            foreach(ImgsProcess process in processList)
            {
                //一旦开始了运行，再修改的参数就不影响了
                process.SetCompressLevel(this.CompressLevel, this.MaxWidth, this.MaxHeight);
            }


            Thread[] threads = processList.Select(x => new Thread(x.Start)).ToArray();
            foreach (Thread thread in threads)
            {
                thread.Start();
                
            }
        }

        /// <summary>
        /// 等待所有线程结束之后再弹出Report
        /// </summary>
        void StopAsync()
        {
            while (true)
            {
                int stopCount = 0;
                foreach (ImgsProcess process in processList)
                {
                    if (process.Stoped) stopCount++;
                    else Thread.Sleep(100);
                }
                if (stopCount == processList.Length) break;
            }
            progress.stopwatch.Stop();
            owner.ShowReport();
        }
        /// <summary>
        /// 停止所有计算。
        /// 先设定ToStop状态
        /// 然后等所有线程计算结束后再停止弹出Report
        /// </summary>
        public void Stop()
        {
            foreach (ImgsProcess process in processList)
            {
                if (process.ToStop) return;
                process.ToStop = true;
            }
            owner.UpdateRunButtonText();
            // 等待所有线程结束之后再弹出Report
            new Thread(StopAsync).Start();
        }
        /// <summary>
        /// 多线程分工方法
        /// </summary>
        /// <returns></returns>
        private ImgsProcess[] DivideIntoMultiProcess()
        {
            List<ImgsProcess> imgsProcess = new List<ImgsProcess>();
            List<List<ImgPaths>> splitImgPath = new List<List<ImgPaths>>();
            for (int t = 0; t < ThreadCount; t++)
            {
                splitImgPath.Add(new List<ImgPaths>());
            }
            int count = ImgPathList.Count;

            for (int i = 0; i < count; i++)
            {
                int threadIndex = i % ThreadCount;
                splitImgPath[threadIndex].Add(ImgPathList[i]);
            }

            int index = 0;
            for (int t = 0; t < ThreadCount; t++)
            {
                if (splitImgPath[t].Count > 0)
                {
                    imgsProcess.Add(new ImgsProcess(index++, splitImgPath[t], progress));
                }
            }

            return imgsProcess.ToArray();
        }

        #endregion
    }
}
